
/*******************************************************************************
 * url.cpp - an url hanlder library
 * Copyright (C) 2006 Nathaniel McCallum <nathaniel@natemccallum.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 ******************************************************************************/

#include <cstring>
#include <vector>
#include <iostream>
#include <sstream> 

#include "bocat/common/url.hpp"

namespace bocat {
namespace common {

using namespace std;


///////////////////////////////////////////////////////////////////////
//


URL::URL(const string& url)
{
        setURL(url);
}

URL::URL(const string& host, unsigned port, const string& path)
{
		this->schema = "http";
        this->host = host;
        this->port = port;
        this->path = path;
}



///////////////////////////////////////////////////////////////////////
//

string URL::getURL()
{
	ostringstream url;
	url << schema << "://" << host;

	if (port != 80)
	{
			url << ":" << port;
	}

	url << path;

	return url.str();
}


/*
string
URL::getReversedHostName( void )
{
	vector< string >	fields;

	string::size_type	pos = 0;
	string::size_type	dot = string::npos;
	while( true ) {
		dot = host.find( '.', pos );
		if ( dot == string::npos ) {
			fields.push_back( host.substr( pos ) );
			break;
		}
		else {
			fields.push_back( host.substr( pos, dot - pos ) );
		}

		pos = dot + 1;
	}

	ostringstream	hostname_reversed;
	for(int i=0; i<fields.size(); ++i) {
		if (i > 0) {
			hostname_reversed << '.';
		}
		hostname_reversed << fields[ fields.size() - i - 1 ];
	}

	return hostname_reversed.str();
}
*/

static
string	
reverse( const string& hostname )
{
	char	buf[ 256 ];
	if (hostname.size() > sizeof(buf)) {
		// too long hostname
		return	hostname;
	}

	int	sz = 0;
	char*	pb = buf;
	const char*	p = hostname.c_str() + hostname.size() - 1;
	for (int i=hostname.size() - 1; i>=0; --i) {
		if ( (*p) == '.' ) {
			strncpy(pb, p + 1, sz);
			pb += sz;
			*pb = '.';
			++pb;

			sz = 0;
		}
		else {
			++sz;
		}

		--p;
	}
	strncpy( pb, hostname.c_str(), sz );

	bool	is_ipv4 = true;
	pb = buf;
	for (unsigned i=0; i<hostname.size(); ++i) {
		if ('.' == *pb) {
			break;
		}
		if ( !(('0' <= *pb)  && (*pb <= '9')) ) {
			is_ipv4 = false;
			break;
		}
	}
	if ( is_ipv4 ) {
		return hostname;
	}

	string	result(buf, hostname.size());
	return result;
}


string
URL::getReversedHostName( void )
{
	return	reverse( host );
}


string
URL::getReversedURL( void )
{
	ostringstream url;
	url << getReversedHostName();

	if (port != 80)
	{
			url << ":" << port;
	}

	url << path;

	return url.str();
}

///////////////////////////////////////////////////////////////////////
//

void URL::setURL(const URL& url)
{
        this->host = url.host;
        this->port = url.port;
        this->path = url.path;
}

void URL::setURL(const string& url)
{
	string::size_type	schema_end = url.find("://");

	if ( schema_end == string::npos || url.length() <= 7)
	{
		std::ostringstream	msg;
		msg << "Malformed URL: " << url;
		throw runtime_error( msg.str() );
	}
	schema = url.substr(0, schema_end);

	string::size_type begin = schema_end + 3;

	string::size_type auth_at = url.find('@', begin);
	if ( auth_at != string::npos ) {
		// auth = url.substr( begin, auth_at - begin );
		begin = auth_at + 1;
	}

	string::size_type colon = url.find(':', begin);
	string::size_type end = url.find('/', begin);

	if (end == string::npos)
	{
		if (colon != string::npos)
		{
			host = url.substr(begin, colon - begin);

			istringstream number((url.substr(colon + 1)));

			number >> port;
		}
		else
		{
			host = url.substr(begin);
			port = 80;
		}

		path = "/";
	}
	else
	{
		if (colon != string::npos && colon < end)
		{
			host = url.substr(begin, colon - begin);

			istringstream number((url.substr(colon + 1, end - colon - 1)));

			number >> port;
		}
		else
		{
			host = url.substr(begin, end - begin);
			port = 80;
		}

		path = url.substr(end);
	}
}

void URL::setPath(const string& path)
{
        if (path.find('/') != 0)
        {
                this->path = "/" + path;
        }
        else
        {
                this->path = path;
        }
}


///////////////////////////////////////////////////////////////////////
//

bool
URL::isURL( const string& str )
{
	return	isURL( str.c_str() );
}

bool
URL::isURL( const char * str )
{
	if (NULL == str) {
		return	false;
	}

	const char*	p = strstr( str, "://" );
	if ( NULL == p ) {
		return	false;
	}

	return	((p - str) < 10);
}


///////////////////////////////////////////////////////////////////////
//

ostream& operator<<(ostream& os, URL& data)
{
        os << data.getURL();

        return os;
}


///////////////////////////////////////////////////////////////////////
//

string getLink(string link, URL& url)
{
        string hyperlink = "";

        if (link.find('#') != string::npos)
        {
                unsigned pound = link.find('#');
                link.erase(pound);
        }

        if (link.find("://") != string::npos)
        {
                if (link.find("http://") == 0 && link.length() > 7) hyperlink = link;
        }
        else if (link.find("mailto:") == 0)
        {
                // do nothing we are not evil spammers!
        }
        else if (link.find("news:") == 0)
        {
                // do nothing this isn't Google Groups
        }
        else if (link.find("//") == 0)
        {
                hyperlink = "http:" + link;
        }
        else if (link.find('/') == 0)
        {
                hyperlink = url.getURL();

                unsigned path = hyperlink.find('/', 7);
                hyperlink.erase(path);

                hyperlink += link;
        }
        else if (link == "")
        {
                // a blank link is useless
        }
        else
        {
                hyperlink = url.getURL();
                string path = url.getPath();

                unsigned cutoff = hyperlink.rfind(path);
                hyperlink.erase(cutoff);

                unsigned dir = path.rfind('/') + 1;
                path.erase(dir);

                while (link.find("../") == 0)
                {
                        unsigned dot = path.rfind('/') - 1;
                        unsigned up = path.rfind('/', dot) + 1;

                        path.erase(up);
                        link.erase(0, 3);
                }
                while (link.find("./") == 0)
                {
                        link.erase(0, 2);
                }

                hyperlink += path + link;
        }

        return hyperlink;
}

}	// namespace common
}	// namespace bocat

